package com.itaming.lycheeframework.support.cipher;

import com.itaming.lycheeframework.support.cipher.asymmetric.RsaEncryptor;
import com.itaming.lycheeframework.support.cipher.asymmetric.Sm2Encryptor;
import com.itaming.lycheeframework.support.cipher.symmetric.AesEncryptor;
import com.itaming.lycheeframework.support.cipher.symmetric.DesEncryptor;
import com.itaming.lycheeframework.support.cipher.symmetric.Sm4Encryptor;
import com.itaming.lycheeframework.support.utils.StringPool;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.Arrays;

/**
 * 加解密类型
 *
 * @author A.Ming
 */
@Getter
@RequiredArgsConstructor
public enum CipherAlgorithm {

    /**
     * AES
     */
    AES_128_ECB(CipherConstant.ALGORITHM_AES, 128, Mode.ECB, Padding.PKCS7, AesEncryptor.class),
    AES_128_ECB_NO_PADDING(CipherConstant.ALGORITHM_AES, 128, Mode.ECB, Padding.NO, AesEncryptor.class),
    AES_192_ECB(CipherConstant.ALGORITHM_AES, 192, Mode.ECB, Padding.PKCS7, AesEncryptor.class),
    AES_192_ECB_NO_PADDING(CipherConstant.ALGORITHM_AES, 192, Mode.ECB, Padding.NO, AesEncryptor.class),
    AES_256_ECB(CipherConstant.ALGORITHM_AES, 256, Mode.ECB, Padding.PKCS7, AesEncryptor.class),
    AES_256_ECB_NO_PADDING(CipherConstant.ALGORITHM_AES, 256, Mode.ECB, Padding.NO, AesEncryptor.class),
    AES_128_CBC(CipherConstant.ALGORITHM_AES, 128, Mode.CBC, Padding.PKCS7, AesEncryptor.class),
    AES_128_CBC_NO_PADDING(CipherConstant.ALGORITHM_AES, 128, Mode.CBC, Padding.NO, AesEncryptor.class),
    AES_192_CBC(CipherConstant.ALGORITHM_AES, 192, Mode.CBC, Padding.PKCS7, AesEncryptor.class),
    AES_192_CBC_NO_PADDING(CipherConstant.ALGORITHM_AES, 192, Mode.CBC, Padding.NO, AesEncryptor.class),
    AES_256_CBC(CipherConstant.ALGORITHM_AES, 256, Mode.CBC, Padding.PKCS7, AesEncryptor.class),
    AES_256_CBC_NO_PADDING(CipherConstant.ALGORITHM_AES, 256, Mode.CBC, Padding.NO, AesEncryptor.class),

    /**
     * DES
     */
    DES_ECB(CipherConstant.ALGORITHM_DES, 64, Mode.ECB, Padding.PKCS7, DesEncryptor.class),
    DES_ECB_NO_PADDING(CipherConstant.ALGORITHM_DES, 64, Mode.ECB, Padding.NO, DesEncryptor.class),
    DES_CBC(CipherConstant.ALGORITHM_DES, 64, Mode.CBC, Padding.PKCS7, DesEncryptor.class),
    DES_CBC_NO_PADDING(CipherConstant.ALGORITHM_DES, 64, Mode.CBC, Padding.NO, DesEncryptor.class),

    /**
     * SM4
     */
    SM4_ECB(CipherConstant.ALGORITHM_SM4, 128, Mode.ECB, Padding.PKCS7, Sm4Encryptor.class),
    SM4_ECB_NO_PADDING(CipherConstant.ALGORITHM_SM4, 128, Mode.ECB, Padding.NO, Sm4Encryptor.class),
    SM4_CBC(CipherConstant.ALGORITHM_SM4, 128, Mode.CBC, Padding.PKCS7, Sm4Encryptor.class),
    SM4_CBC_NO_PADDING(CipherConstant.ALGORITHM_SM4, 128, Mode.CBC, Padding.NO, Sm4Encryptor.class),

    /**
     * RSA
     */
    RSA(CipherConstant.ALGORITHM_RSA, 0, Mode.ECB, Padding.PKCS1, RsaEncryptor.class),

    /**
     * SM2
     */
    SM2(CipherConstant.ALGORITHM_SM2, 0, null, null, Sm2Encryptor.class),
    SM2_C1C2C3(CipherConstant.ALGORITHM_SM2_C1C2C3, 0, null, null, Sm2Encryptor.class),

    ;

    /**
     * 加密算法
     */
    private final String algorithm;

    /**
     * 密钥长度，单位：bit
     */
    private final int keyBitLength;

    /**
     * 工作模式
     */
    private final Mode workingMode;

    /**
     * 填充
     */
    private final Padding padding;

    /**
     * 加密器类
     */
    private final Class<? extends Encryptor<?>> encryptorClass;

    /**
     * 获取指定的枚举
     * <p>
     * 将传入的加密方法名称中的横线(-)转为下划线(_)，并与枚举名称进行匹配，如：AES-128-ECB 转为 AES_128_ECB
     *
     * @param methodName 加密方法名称
     * @return CryptoMode
     */
    public static CipherAlgorithm of(String methodName) {
        return Arrays.stream(CipherAlgorithm.values())
            .filter(e -> e.name().replaceAll("_", "-").equalsIgnoreCase(methodName))
            .findFirst()
            .orElse(null);
    }

    /**
     * 是否ECB模式
     *
     * @return boolean
     */
    public boolean isEcb() {
        return Mode.ECB.equals(workingMode);
    }

    /**
     * 是否CBC模式
     *
     * @return boolean
     */
    public boolean isCbc() {
        return Mode.CBC.equals(workingMode);
    }

    /**
     * 获取算法全称
     *
     * @return 算法全称
     */
    public String getFullAlgorithm() {
        StringBuilder sb = new StringBuilder(algorithm);

        if (workingMode != null) {
            sb.append(StringPool.SLASH);
            sb.append(workingMode.getName());
        }

        if (padding != null) {
            sb.append(StringPool.SLASH);
            sb.append(padding.getName());
        }

        return sb.toString();
    }

    /**
     * 填充枚举类
     */
    @Getter
    @RequiredArgsConstructor
    public enum Padding {

        NO("NoPadding"),
        PKCS1("PKCS1Padding"),
        PKCS5("PKCS5Padding"),
        PKCS7("PKCS7Padding"),
        ;

        private final String name;

    }

    /**
     * 模式枚举类
     */
    @Getter
    @RequiredArgsConstructor
    public enum Mode {

        ECB("ECB"),
        CBC("CBC"),
        ;

        private final String name;

    }
}
